package hc.bre.log

import java.time.LocalDateTime
import org.slf4j.*

@groovy.transform.CompileStatic
class RuleLogger {
	private static final Logger _log = LoggerFactory.getLogger(RuleLogger.class);

	static private ThreadLocal<RuleLogger> logger= new ThreadLocal<RuleLogger>();

	static public RuleLogger get() {
		def lg = logger.get();
		if(lg == null ) {
			logger.set(new RuleLogger());
			return logger.get();
		}
		return lg;
	}

	LinkedList<RuleLog> ruleCalledStack = new LinkedList<RuleLog>();
	RootRuleLog rootRuleLog;

	void enter(Object owner,Closure rule) {
		def top = ruleCalledStack.peek();
		def log;

		if(top == null) {
			log = new RootRuleLog(rule);
			rootRuleLog= log;
			//TODO owner maybe not the instance script in embedded environment
			if(owner instanceof groovy.lang.Script) {
				log.variables = owner.binding.variables;
			}
			else {
				_log.error "No variables because it don't started in cript Object."+ owner.getClass().getName();
			}
		}
		else {
			log = new RuleLog(rule);
			top.addLogIntoDetails(log);
		}

		ruleCalledStack.push(log)
	}
	void exit(result) {
		def log = ruleCalledStack.pop();
		if(log ==null) {
			_log.error " the rule called stack is empty when  exit!"
			return;
		}
		log.result = result;
		log.status='completed'
		log.nanoTimeEnded = System.nanoTime();

		saveLogIfFinished(log)
	}

	void exception(Throwable ex) {
		RuleLog log = ruleCalledStack.pop();
		
		if(log ==null) {
			_log.error "apply methods catch a exception, but the rule called stack is empty."
			return;
		};
		
		def Map rst = [
			exceptionClass:ex.getClass().getName(),
			message:ex.message,
			stackTrace:hc.Util.stackTraceText(ex)
		];

		log.result = rst;

		log.nanoTimeEnded = System.nanoTime();
		log.status='exception';
		saveLogIfFinished(log);
	}

	void saveLogIfFinished(RuleLog log) {

		if(ruleCalledStack.peek() !=null) return;

		rootRuleLog = null;

		RootRuleLog rl = log as RootRuleLog;
		rl.endTime = LocalDateTime.now();

		LogStore.logStore.save(rl)
	}

	void output(msg)
	{
		OutputItem item = new OutputItem();
		item.ruleClass = ruleCalledStack.peek().ruleClass;
		item.content = msg;

		if(rootRuleLog.outputs==null) rootRuleLog.outputs = [];

		rootRuleLog.outputs.add(item);
	}
}
